<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#module-的语法"  class="anchor" id="user-content-module-的语法">

    </a>Module 的语法
    </h1>
    <h2><a href="#概述"  class="anchor" id="user-content-概述">

    </a>概述
    </h2>
    <p>历史上，JavaScript 一直没有模块（module）体系，无法将一个大程序拆分成互相依赖的小文件，再用简单的方法拼装起来。其他语言都有这项功能，比如 Ruby 的<code>require</code>、Python 的<code>import</code>，甚至就连
        CSS 都有<code>@import</code>，但是 JavaScript 任何这方面的支持都没有，这对开发大型的、复杂的项目形成了巨大障碍。</p>
    <p>在 ES6 之前，社区制定了一些模块加载方案，最主要的有 CommonJS 和 AMD 两种。前者用于服务器，后者用于浏览器。ES6 在语言标准的层面上，实现了模块功能，而且实现得相当简单，完全可以取代 CommonJS 和
        AMD 规范，成为浏览器和服务器通用的模块解决方案。</p>
    <p>ES6 模块的设计思想，是尽量的静态化，使得编译时就能确定模块的依赖关系，以及输入和输出的变量。CommonJS 和 AMD 模块，都只能在运行时确定这些东西。比如，CommonJS
        模块就是对象，输入时必须查找对象属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> CommonJS模块</span>
<span class="pl-k">let</span> { stat, exists, readFile } <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span
                class="pl-s"><span class="pl-pds">'</span>fs<span class="pl-pds">'</span></span>);

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">let</span> _fs <span class="pl-k">=</span> <span class="pl-c1">require</span>(<span
                class="pl-s"><span class="pl-pds">'</span>fs<span class="pl-pds">'</span></span>);
<span class="pl-k">let</span> stat <span class="pl-k">=</span> <span class="pl-smi">_fs</span>.<span
                class="pl-smi">stat</span>;
<span class="pl-k">let</span> exists <span class="pl-k">=</span> <span class="pl-smi">_fs</span>.<span class="pl-smi">exists</span>;
<span class="pl-k">let</span> readfile <span class="pl-k">=</span> <span class="pl-smi">_fs</span>.<span class="pl-smi">readfile</span>;</pre>
    </div>
    <p>上面代码的实质是整体加载<code>fs</code>模块（即加载<code>fs</code>的所有方法），生成一个对象（<code>_fs</code>），然后再从这个对象上面读取 3
        个方法。这种加载称为“运行时加载”，因为只有运行时才能得到这个对象，导致完全没办法在编译时做“静态优化”。</p>
    <p>ES6 模块不是对象，而是通过<code>export</code>命令显式指定输出的代码，再通过<code>import</code>命令输入。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> ES6模块</span>
<span class="pl-k">import</span> { <span class="pl-smi">stat</span>, <span class="pl-smi">exists</span>, <span
                class="pl-smi">readFile</span> } <span class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>fs<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码的实质是从<code>fs</code>模块加载 3 个方法，其他方法不加载。这种加载称为“编译时加载”或者静态加载，即 ES6 可以在编译时就完成模块加载，效率要比 CommonJS
        模块的加载方式高。当然，这也导致了没法引用 ES6 模块本身，因为它不是对象。</p>
    <p>由于 ES6 模块是编译时加载，使得静态分析成为可能。有了它，就能进一步拓宽 JavaScript 的语法，比如引入宏（macro）和类型检验（type system）这些只能靠静态分析实现的功能。</p>
    <p>除了静态加载带来的各种好处，ES6 模块还有以下好处。</p>
    <ul>
        <li>不再需要<code>UMD</code>模块格式了，将来服务器和浏览器都会支持 ES6 模块格式。目前，通过各种工具库，其实已经做到了这一点。</li>
        <li>将来浏览器的新 API 就能用模块格式提供，不再必须做成全局变量或者<code>navigator</code>对象的属性。</li>
        <li>不再需要对象作为命名空间（比如<code>Math</code>对象），未来这些功能可以通过模块提供。</li>
    </ul>
    <p>本章介绍 ES6 模块的语法，下一章介绍如何在浏览器和 Node 之中，加载 ES6 模块。</p>
    <h2><a href="#严格模式"  class="anchor" id="user-content-严格模式">

    </a>严格模式
    </h2>
    <p>ES6 的模块自动采用严格模式，不管你有没有在模块头部加上<code>"use strict";</code>。</p>
    <p>严格模式主要有以下限制。</p>
    <ul>
        <li>变量必须声明后再使用</li>
        <li>函数的参数不能有同名属性，否则报错</li>
        <li>不能使用<code>with</code>语句</li>
        <li>不能对只读属性赋值，否则报错</li>
        <li>不能使用前缀 0 表示八进制数，否则报错</li>
        <li>不能删除不可删除的属性，否则报错</li>
        <li>不能删除变量<code>delete prop</code>，会报错，只能删除属性<code>delete global[prop]</code></li>
        <li><code>eval</code>不会在它的外层作用域引入变量</li>
        <li><code>eval</code>和<code>arguments</code>不能被重新赋值</li>
        <li><code>arguments</code>不会自动反映函数参数的变化</li>
        <li>不能使用<code>arguments.callee</code></li>
        <li>不能使用<code>arguments.caller</code></li>
        <li>禁止<code>this</code>指向全局对象</li>
        <li>不能使用<code>fn.caller</code>和<code>fn.arguments</code>获取函数调用的堆栈</li>
        <li>增加了保留字（比如<code>protected</code>、<code>static</code>和<code>interface</code>）</li>
    </ul>
    <p>上面这些限制，模块都必须遵守。由于严格模式是 ES5 引入的，不属于 ES6，所以请参阅相关 ES5 书籍，本书不再详细介绍了。</p>
    <p>其中，尤其需要注意<code>this</code>的限制。ES6 模块之中，顶层的<code>this</code>指向<code>undefined</code>，即不应该在顶层代码使用<code>this</code>。
    </p>
    <h2><a href="#export-命令"  class="anchor" id="user-content-export-命令">

    </a>export 命令
    </h2>
    <p>模块功能主要由两个命令构成：<code>export</code>和<code>import</code>。<code>export</code>命令用于规定模块的对外接口，<code>import</code>命令用于输入其他模块提供的功能。
    </p>
    <p>一个模块就是一个独立的文件。该文件内部的所有变量，外部无法获取。如果你希望外部能够读取模块内部的某个变量，就必须使用<code>export</code>关键字输出该变量。下面是一个 JS 文件，里面使用<code>export</code>命令输出变量。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> profile.js</span>
<span class="pl-k">export</span> <span class="pl-k">var</span> firstName <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>Michael<span class="pl-pds">'</span></span>;
<span class="pl-k">export</span> <span class="pl-k">var</span> lastName <span class="pl-k">=</span> <span
                class="pl-s"><span class="pl-pds">'</span>Jackson<span class="pl-pds">'</span></span>;
<span class="pl-k">export</span> <span class="pl-k">var</span> year <span class="pl-k">=</span> <span
                class="pl-c1">1958</span>;</pre>
    </div>
    <p>上面代码是<code>profile.js</code>文件，保存了用户信息。ES6 将其视为一个模块，里面用<code>export</code>命令对外部输出了三个变量。</p>
    <p><code>export</code>的写法，除了像上面这样，还有另外一种。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> profile.js</span>
<span class="pl-k">var</span> firstName <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Michael<span
                class="pl-pds">'</span></span>;
<span class="pl-k">var</span> lastName <span class="pl-k">=</span> <span class="pl-s"><span class="pl-pds">'</span>Jackson<span
                class="pl-pds">'</span></span>;
<span class="pl-k">var</span> year <span class="pl-k">=</span> <span class="pl-c1">1958</span>;

<span class="pl-k">export</span> {<span class="pl-smi">firstName</span>, <span class="pl-smi">lastName</span>, <span
                class="pl-smi">year</span>};</pre>
    </div>
    <p>上面代码在<code>export</code>命令后面，使用大括号指定所要输出的一组变量。它与前一种写法（直接放置在<code>var</code>语句前）是等价的，但是应该优先考虑使用这种写法。因为这样就可以在脚本尾部，一眼看清楚输出了哪些变量。
    </p>
    <p><code>export</code>命令除了输出变量，还可以输出函数或类（class）。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> <span class="pl-k">function</span> <span
            class="pl-en">multiply</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
  <span class="pl-k">return</span> x <span class="pl-k">*</span> y;
};</pre>
    </div>
    <p>上面代码对外输出一个函数<code>multiply</code>。</p>
    <p>通常情况下，<code>export</code>输出的变量就是本来的名字，但是可以使用<code>as</code>关键字重命名。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">v1</span>() { <span
            class="pl-k">...</span> }
<span class="pl-k">function</span> <span class="pl-en">v2</span>() { <span class="pl-k">...</span> }

<span class="pl-k">export</span> {
  <span class="pl-smi">v1</span> <span class="pl-k">as</span> <span class="pl-smi">streamV1</span>,
  <span class="pl-smi">v2</span> <span class="pl-k">as</span> <span class="pl-smi">streamV2</span>,
  <span class="pl-smi">v2</span> <span class="pl-k">as</span> <span class="pl-smi">streamLatestVersion</span>
};</pre>
    </div>
    <p>上面代码使用<code>as</code>关键字，重命名了函数<code>v1</code>和<code>v2</code>的对外接口。重命名后，<code>v2</code>可以用不同的名字输出两次。</p>
    <p>需要特别注意的是，<code>export</code>命令规定的是对外的接口，必须与模块内部的变量建立一一对应关系。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">export</span> <span class="pl-c1">1</span>;

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">var</span> m <span class="pl-k">=</span> <span class="pl-c1">1</span>;
<span class="pl-k">export</span> <span class="pl-smi">m</span>;</pre>
    </div>
    <p>上面两种写法都会报错，因为没有提供对外的接口。第一种写法直接输出 1，第二种写法通过变量<code>m</code>，还是直接输出 1。<code>1</code>只是一个值，不是接口。正确的写法是下面这样。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 写法一</span>
<span class="pl-k">export</span> <span class="pl-k">var</span> m <span class="pl-k">=</span> <span
                class="pl-c1">1</span>;

<span class="pl-c"><span class="pl-c">//</span> 写法二</span>
<span class="pl-k">var</span> m <span class="pl-k">=</span> <span class="pl-c1">1</span>;
<span class="pl-k">export</span> {<span class="pl-smi">m</span>};

<span class="pl-c"><span class="pl-c">//</span> 写法三</span>
<span class="pl-k">var</span> n <span class="pl-k">=</span> <span class="pl-c1">1</span>;
<span class="pl-k">export</span> {<span class="pl-smi">n</span> <span class="pl-k">as</span> <span
                class="pl-smi">m</span>};</pre>
    </div>
    <p>上面三种写法都是正确的，规定了对外的接口<code>m</code>。其他脚本可以通过这个接口，取到值<code>1</code>。它们的实质是，在接口名与模块内部变量之间，建立了一一对应的关系。</p>
    <p>同样的，<code>function</code>和<code>class</code>的输出，也必须遵守这样的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">function</span> <span class="pl-en">f</span>() {}
<span class="pl-k">export</span> <span class="pl-smi">f</span>;

<span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">f</span>() {};

<span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">function</span> <span class="pl-en">f</span>() {}
<span class="pl-k">export</span> {<span class="pl-smi">f</span>};</pre>
    </div>
    <p>另外，<code>export</code>语句输出的接口，与其对应的值是动态绑定关系，即通过该接口，可以取到模块内部实时的值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> <span
            class="pl-k">var</span> foo <span class="pl-k">=</span> <span class="pl-s"><span
            class="pl-pds">'</span>bar<span class="pl-pds">'</span></span>;
<span class="pl-c1">setTimeout</span>(() <span class="pl-k">=&gt;</span> foo <span class="pl-k">=</span> <span
                class="pl-s"><span class="pl-pds">'</span>baz<span class="pl-pds">'</span></span>, <span class="pl-c1">500</span>);</pre>
    </div>
    <p>上面代码输出变量<code>foo</code>，值为<code>bar</code>，500 毫秒之后变成<code>baz</code>。</p>
    <p>这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存，不存在动态更新，详见下文《Module 的加载实现》一节。</p>
    <p>最后，<code>export</code>命令可以出现在模块的任何位置，只要处于模块顶层就可以。如果处于块级作用域内，就会报错，下一节的<code>import</code>命令也是如此。这是因为处于条件代码块之中，就没法做静态优化了，违背了
        ES6 模块的设计初衷。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span class="pl-en">foo</span>() {
  <span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-s"><span class="pl-pds">'</span>bar<span
                class="pl-pds">'</span></span> <span class="pl-c"><span class="pl-c">//</span> SyntaxError</span>
}
<span class="pl-en">foo</span>()</pre>
    </div>
    <p>上面代码中，<code>export</code>语句放在函数之中，结果报错。</p>
    <h2><a href="#import-命令"  class="anchor" id="user-content-import-命令">

    </a>import 命令
    </h2>
    <p>使用<code>export</code>命令定义了模块的对外接口以后，其他 JS 文件就可以通过<code>import</code>命令加载这个模块。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> main.js</span>
<span class="pl-k">import</span> {<span class="pl-smi">firstName</span>, <span class="pl-smi">lastName</span>, <span
                class="pl-smi">year</span>} <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>./profile<span class="pl-pds">'</span></span>;

<span class="pl-k">function</span> <span class="pl-en">setName</span>(<span class="pl-smi">element</span>) {
  <span class="pl-smi">element</span>.<span class="pl-smi">textContent</span> <span
                class="pl-k">=</span> firstName <span class="pl-k">+</span> <span class="pl-s"><span
                class="pl-pds">'</span> <span class="pl-pds">'</span></span> <span class="pl-k">+</span> lastName;
}</pre>
    </div>
    <p>上面代码的<code>import</code>命令，用于加载<code>profile.js</code>文件，并从中输入变量。<code>import</code>命令接受一对大括号，里面指定要从其他模块导入的变量名。大括号里面的变量名，必须与被导入模块（<code>profile.js</code>）对外接口的名称相同。
    </p>
    <p>如果想为输入的变量重新取一个名字，<code>import</code>命令要使用<code>as</code>关键字，将输入的变量重命名。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span> { <span class="pl-smi">lastName</span> <span class="pl-k">as</span> <span
                class="pl-smi">surname</span> } <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>./profile<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p><code>import</code>后面的<code>from</code>指定模块文件的位置，可以是相对路径，也可以是绝对路径，<code>.js</code>后缀可以省略。如果只是模块名，不带有路径，那么必须有配置文件，告诉
        JavaScript 引擎该模块的位置。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span> {<span class="pl-smi">myMethod</span>} <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>util<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码中，<code>util</code>是模块文件名，由于不带有路径，必须通过配置，告诉引擎怎么取到这个模块。</p>
    <p>注意，<code>import</code>命令具有提升效果，会提升到整个模块的头部，首先执行。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-en">foo</span>();

<span class="pl-k">import</span> { <span class="pl-smi">foo</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>my_module<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面的代码不会报错，因为<code>import</code>的执行早于<code>foo</code>的调用。这种行为的本质是，<code>import</code>命令是编译阶段执行的，在代码运行之前。</p>
    <p>由于<code>import</code>是静态执行，所以不能使用表达式和变量，这些只有在运行时才能得到结果的语法结构。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">import</span> { '<span class="pl-smi">f</span>' + '<span class="pl-smi">oo</span>' } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
                class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">let</span> <span class="pl-c1">module</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>my_module<span class="pl-pds">'</span></span>;
<span class="pl-k">import</span> { <span class="pl-smi">foo</span> } <span class="pl-k">from</span> module;

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">if</span> (x <span class="pl-k">===</span> <span class="pl-c1">1</span>) {
  <span class="pl-k">import</span> { <span class="pl-smi">foo</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>module1<span class="pl-pds">'</span></span>;
} <span class="pl-k">else</span> {
  <span class="pl-k">import</span> { <span class="pl-smi">foo</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>module2<span class="pl-pds">'</span></span>;
}</pre>
    </div>
    <p>上面三种写法都会报错，因为它们用到了表达式、变量和<code>if</code>结构。在静态分析阶段，这些语法都是没法得到值的。</p>
    <p>最后，<code>import</code>语句会执行所加载的模块，因此可以有下面的写法。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span> <span class="pl-s"><span class="pl-pds">'</span>lodash<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码仅仅执行<code>lodash</code>模块，但是不输入任何值。</p>
    <p>如果多次重复执行同一句<code>import</code>语句，那么只会执行一次，而不会执行多次。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-s"><span
            class="pl-pds">'</span>lodash<span class="pl-pds">'</span></span>;
<span class="pl-k">import</span> <span class="pl-s"><span class="pl-pds">'</span>lodash<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码加载了两次<code>lodash</code>，但是只会执行一次。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> { <span class="pl-smi">foo</span> } <span
            class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
            class="pl-pds">'</span></span>;
<span class="pl-k">import</span> { <span class="pl-smi">bar</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>my_module<span class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">import</span> { <span class="pl-smi">foo</span>, <span class="pl-smi">bar</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码中，虽然<code>foo</code>和<code>bar</code>在两个语句中加载，但是它们对应的是同一个<code>my_module</code>实例。也就是说，<code>import</code>语句是
        Singleton 模式。</p>
    <p>目前阶段，通过 Babel 转码，CommonJS 模块的<code>require</code>命令和 ES6 模块的<code>import</code>命令，可以写在同一个模块里面，但是最好不要这样做。因为<code>import</code>在静态解析阶段执行，所以它是一个模块之中最早执行的。下面的代码可能不会得到预期结果。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">require</span>(<span class="pl-s"><span
            class="pl-pds">'</span>core-js/modules/es6.symbol<span class="pl-pds">'</span></span>);
<span class="pl-c1">require</span>(<span class="pl-s"><span class="pl-pds">'</span>core-js/modules/es6.promise<span
                class="pl-pds">'</span></span>);
<span class="pl-k">import</span> <span class="pl-smi">React</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>React<span class="pl-pds">'</span></span>;</pre>
    </div>
    <h2><a href="#模块的整体加载"  class="anchor" id="user-content-模块的整体加载">

    </a>模块的整体加载
    </h2>
    <p>除了指定加载某个输出值，还可以使用整体加载，即用星号（<code>*</code>）指定一个对象，所有输出值都加载在这个对象上面。</p>
    <p>下面是一个<code>circle.js</code>文件，它输出两个方法<code>area</code>和<code>circumference</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> circle.js</span>

<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">area</span>(<span
                class="pl-smi">radius</span>) {
  <span class="pl-k">return</span> <span class="pl-c1">Math</span>.<span class="pl-c1">PI</span> <span
                class="pl-k">*</span> radius <span class="pl-k">*</span> radius;
}

<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">circumference</span>(<span
                class="pl-smi">radius</span>) {
  <span class="pl-k">return</span> <span class="pl-c1">2</span> <span class="pl-k">*</span> <span
                class="pl-c1">Math</span>.<span class="pl-c1">PI</span> <span class="pl-k">*</span> radius;
}</pre>
    </div>
    <p>现在，加载这个模块。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> main.js</span>

<span class="pl-k">import</span> { <span class="pl-smi">area</span>, <span class="pl-smi">circumference</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>./circle<span
                class="pl-pds">'</span></span>;

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>圆面积：<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span
                class="pl-en">area</span>(<span class="pl-c1">4</span>));
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>圆周长：<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span
                class="pl-en">circumference</span>(<span class="pl-c1">14</span>));</pre>
    </div>
    <p>上面写法是逐一指定要加载的方法，整体加载的写法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-c1">*</span> <span
            class="pl-k">as</span> <span class="pl-smi">circle</span> <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>./circle<span class="pl-pds">'</span></span>;

<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>圆面积：<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span
                class="pl-smi">circle</span>.<span class="pl-en">area</span>(<span class="pl-c1">4</span>));
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>圆周长：<span class="pl-pds">'</span></span> <span class="pl-k">+</span> <span
                class="pl-smi">circle</span>.<span class="pl-en">circumference</span>(<span
                class="pl-c1">14</span>));</pre>
    </div>
    <p>注意，模块整体加载所在的那个对象（上例是<code>circle</code>），应该是可以静态分析的，所以不允许运行时改变。下面的写法都是不允许的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-c1">*</span> <span
            class="pl-k">as</span> <span class="pl-smi">circle</span> <span class="pl-k">from</span> <span class="pl-s"><span
            class="pl-pds">'</span>./circle<span class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 下面两行都是不允许的</span>
<span class="pl-smi">circle</span>.<span class="pl-smi">foo</span> <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">'</span>hello<span class="pl-pds">'</span></span>;
<span class="pl-smi">circle</span>.<span class="pl-en">area</span> <span class="pl-k">=</span> <span class="pl-k">function</span> () {};</pre>
    </div>
    <h2><a href="#export-default-命令"  class="anchor" id="user-content-export-default-命令">

    </a>export default 命令
    </h2>
    <p>从前面的例子可以看出，使用<code>import</code>命令的时候，用户需要知道所要加载的变量名或函数名，否则无法加载。但是，用户肯定希望快速上手，未必愿意阅读文档，去了解模块有哪些属性和方法。</p>
    <p>为了给用户提供方便，让他们不用阅读文档就能加载模块，就要用到<code>export default</code>命令，为模块指定默认输出。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> export-default.js</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">function</span> () {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
}</pre>
    </div>
    <p>上面代码是一个模块文件<code>export-default.js</code>，它的默认输出是一个函数。</p>
    <p>其他模块加载该模块时，<code>import</code>命令可以为该匿名函数指定任意名字。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> import-default.js</span>
<span class="pl-k">import</span> <span class="pl-smi">customName</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./export-default<span class="pl-pds">'</span></span>;
<span class="pl-en">customName</span>(); <span class="pl-c"><span class="pl-c">//</span> 'foo'</span></pre>
    </div>
    <p>
        上面代码的<code>import</code>命令，可以用任意名称指向<code>export-default.js</code>输出的方法，这时就不需要知道原模块输出的函数名。需要注意的是，这时<code>import</code>命令后面，不使用大括号。
    </p>
    <p><code>export default</code>命令用在非匿名函数前，也是可以的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> export-default.js</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">function</span> <span
                class="pl-en">foo</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
}

<span class="pl-c"><span class="pl-c">//</span> 或者写成</span>

<span class="pl-k">function</span> <span class="pl-en">foo</span>() {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>foo<span class="pl-pds">'</span></span>);
}

<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-smi">foo</span>;</pre>
    </div>
    <p>上面代码中，<code>foo</code>函数的函数名<code>foo</code>，在模块外部是无效的。加载的时候，视同匿名函数加载。</p>
    <p>下面比较一下默认输出和正常输出。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 第一组</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">function</span> <span
                class="pl-en">crc32</span>() { <span class="pl-c"><span class="pl-c">//</span> 输出</span>
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}

<span class="pl-k">import</span> <span class="pl-smi">crc32</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>crc32<span class="pl-pds">'</span></span>; <span class="pl-c"><span
                class="pl-c">//</span> 输入</span>

<span class="pl-c"><span class="pl-c">//</span> 第二组</span>
<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">crc32</span>() { <span
                class="pl-c"><span class="pl-c">//</span> 输出</span>
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
};

<span class="pl-k">import</span> {<span class="pl-smi">crc32</span>} <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>crc32<span class="pl-pds">'</span></span>; <span class="pl-c"><span
                class="pl-c">//</span> 输入</span></pre>
    </div>
    <p>上面代码的两组写法，第一组是使用<code>export default</code>时，对应的<code>import</code>语句不需要使用大括号；第二组是不使用<code>export default</code>时，对应的<code>import</code>语句需要使用大括号。
    </p>
    <p><code>export default</code>命令用于指定模块的默认输出。显然，一个模块只能有一个默认输出，因此<code>export
        default</code>命令只能使用一次。所以，<code>import</code>命令后面才不用加大括号，因为只可能对应一个方法。</p>
    <p>本质上，<code>export default</code>就是输出一个叫做<code>default</code>的变量或方法，然后系统允许你为它取任意名字。所以，下面的写法是有效的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> modules.js</span>
<span class="pl-k">function</span> <span class="pl-en">add</span>(<span class="pl-smi">x</span>, <span
                class="pl-smi">y</span>) {
  <span class="pl-k">return</span> x <span class="pl-k">*</span> y;
}
<span class="pl-k">export</span> {<span class="pl-smi">add</span> <span class="pl-k">as</span> <span class="pl-c1">default</span>};
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c"><span class="pl-c">//</span> export default add;</span>

<span class="pl-c"><span class="pl-c">//</span> app.js</span>
<span class="pl-k">import</span> { <span class="pl-c1">default</span> <span class="pl-k">as</span> <span class="pl-smi">foo</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>modules<span
                class="pl-pds">'</span></span>;
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c"><span class="pl-c">//</span> import foo from 'modules';</span></pre>
    </div>
    <p>正是因为<code>export default</code>命令其实只是输出一个叫做<code>default</code>的变量，所以它后面不能跟变量声明语句。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">export</span> <span class="pl-k">var</span> a <span class="pl-k">=</span> <span
                class="pl-c1">1</span>;

<span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">var</span> a <span class="pl-k">=</span> <span class="pl-c1">1</span>;
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-smi">a</span>;

<span class="pl-c"><span class="pl-c">//</span> 错误</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">var</span> a <span
                class="pl-k">=</span> <span class="pl-c1">1</span>;</pre>
    </div>
    <p>上面代码中，<code>export default a</code>的含义是将变量<code>a</code>的值赋给变量<code>default</code>。所以，最后一种写法会报错。</p>
    <p>同样地，因为<code>export default</code>本质是将该命令后面的值，赋给<code>default</code>变量以后再默认，所以直接将一个值写在<code>export default</code>之后。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 正确</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-c1">42</span>;

<span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">export</span> <span class="pl-c1">42</span>;</pre>
    </div>
    <p>上面代码中，后一句报错是因为没有指定对外的接口，而前一句指定外对接口为<code>default</code>。</p>
    <p>有了<code>export default</code>命令，输入模块时就非常直观了，以输入 lodash 模块为例。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span> <span class="pl-smi">_</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>lodash<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>如果想在一条<code>import</code>语句中，同时输入默认方法和其他接口，可以写成下面这样。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span> <span class="pl-smi">_</span>, { <span class="pl-smi">each</span>, <span
                class="pl-smi">each</span> <span class="pl-k">as</span> <span class="pl-smi">forEach</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>lodash<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>对应上面代码的<code>export</code>语句如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> <span class="pl-c1">default</span> <span
            class="pl-k">function</span> (<span class="pl-smi">obj</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ···</span>
}

<span class="pl-k">export</span> <span class="pl-k">function</span> <span class="pl-en">each</span>(<span
                class="pl-smi">obj</span>, <span class="pl-smi">iterator</span>, <span class="pl-smi">context</span>) {
  <span class="pl-c"><span class="pl-c">//</span> ···</span>
}

<span class="pl-k">export</span> { <span class="pl-smi">each</span> <span class="pl-k">as</span> <span class="pl-smi">forEach</span> };</pre>
    </div>
    <p>上面代码的最后一行的意思是，暴露出<code>forEach</code>接口，默认指向<code>each</code>接口，即<code>forEach</code>和<code>each</code>指向同一个方法。
    </p>
    <p><code>export default</code>也可以用来输出类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> MyClass.js</span>
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">class</span> { <span
                class="pl-k">...</span> }

<span class="pl-c"><span class="pl-c">//</span> main.js</span>
<span class="pl-k">import</span> <span class="pl-smi">MyClass</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>MyClass<span class="pl-pds">'</span></span>;
<span class="pl-k">let</span> o <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyClass</span>();</pre>
    </div>
    <h2><a href="#export-与-import-的复合写法"  class="anchor" id="user-content-export-与-import-的复合写法">

    </a>export 与 import 的复合写法
    </h2>
    <p>如果在一个模块之中，先输入后输出同一个模块，<code>import</code>语句可以与<code>export</code>语句写在一起。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> { <span class="pl-smi">foo</span>, <span
            class="pl-smi">bar</span> } <span class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
            class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">import</span> { <span class="pl-smi">foo</span>, <span class="pl-smi">bar</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
                class="pl-pds">'</span></span>;
<span class="pl-k">export</span> { <span class="pl-smi">foo</span>, <span class="pl-smi">bar</span> };</pre>
    </div>
    <p>上面代码中，<code>export</code>和<code>import</code>语句可以结合在一起，写成一行。</p>
    <p>模块的接口改名和整体输出，也可以采用这种写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 接口改名</span>
<span class="pl-k">export</span> { <span class="pl-smi">foo</span> <span class="pl-k">as</span> <span class="pl-smi">myFoo</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>my_module<span
                class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 整体输出</span>
<span class="pl-k">export</span> <span class="pl-c1">*</span> <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>my_module<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>默认接口的写法如下。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">export</span> { <span class="pl-smi">default</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>foo<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>具名接口改为默认接口的写法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> { <span
            class="pl-smi">es6</span> <span class="pl-k">as</span> <span class="pl-c1">default</span> } <span
            class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>./someModule<span
            class="pl-pds">'</span></span>;

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">import</span> { <span class="pl-smi">es6</span> } <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./someModule<span class="pl-pds">'</span></span>;
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-smi">es6</span>;</pre>
    </div>
    <p>同样地，默认接口也可以改名为具名接口。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">export</span> { <span class="pl-c1">default</span> <span class="pl-k">as</span> <span
                class="pl-smi">es6</span> } <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>./someModule<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>下面三种<code>import</code>语句，没有对应的复合写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span> <span class="pl-c1">*</span> <span
            class="pl-k">as</span> <span class="pl-smi">someIdentifier</span> <span class="pl-k">from</span> <span
            class="pl-s"><span class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;
<span class="pl-k">import</span> <span class="pl-smi">someIdentifier</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;
<span class="pl-k">import</span> <span class="pl-smi">someIdentifier</span>, { <span
                class="pl-smi">namedIdentifier</span> } <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;</pre>
    </div>
    <p>为了做到形式的对称，现在有<a href="https://github.com/leebyron/ecmascript-export-default-from">提案</a>，提出补上这三种复合写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">export</span> <span class="pl-c1">*</span> <span
            class="pl-smi">as</span> <span class="pl-smi">someIdentifier</span> <span class="pl-k">from</span> <span
            class="pl-s"><span class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;
<span class="pl-k">export</span> <span class="pl-smi">someIdentifier</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;
<span class="pl-k">export</span> <span class="pl-smi">someIdentifier</span>, { <span
                class="pl-smi">namedIdentifier</span> } <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">"</span>someModule<span class="pl-pds">"</span></span>;</pre>
    </div>
    <h2><a href="#模块的继承"  class="anchor" id="user-content-模块的继承">

    </a>模块的继承
    </h2>
    <p>模块之间也可以继承。</p>
    <p>假设有一个<code>circleplus</code>模块，继承了<code>circle</code>模块。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> circleplus.js</span>

<span class="pl-k">export</span> <span class="pl-c1">*</span> <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>circle<span class="pl-pds">'</span></span>;
<span class="pl-k">export</span> <span class="pl-k">var</span> e <span class="pl-k">=</span> <span class="pl-c1">2.71828182846</span>;
<span class="pl-k">export</span> <span class="pl-c1">default</span> <span class="pl-k">function</span>(<span
                class="pl-smi">x</span>) {
  <span class="pl-k">return</span> <span class="pl-c1">Math</span>.<span class="pl-c1">exp</span>(x);
}</pre>
    </div>
    <p>上面代码中的<code>export *</code>，表示再输出<code>circle</code>模块的所有属性和方法。注意，<code>export *</code>命令会忽略<code>circle</code>模块的<code>default</code>方法。然后，上面代码又输出了自定义的<code>e</code>变量和默认方法。
    </p>
    <p>这时，也可以将<code>circle</code>的属性或方法，改名后再输出。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> circleplus.js</span>

<span class="pl-k">export</span> { <span class="pl-smi">area</span> <span class="pl-k">as</span> <span class="pl-smi">circleArea</span> } <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>circle<span
                class="pl-pds">'</span></span>;</pre>
    </div>
    <p>上面代码表示，只输出<code>circle</code>模块的<code>area</code>方法，且将其改名为<code>circleArea</code>。</p>
    <p>加载上面模块的写法如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> main.js</span>

<span class="pl-k">import</span> <span class="pl-c1">*</span> <span class="pl-k">as</span> <span
                class="pl-smi">math</span> <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>circleplus<span class="pl-pds">'</span></span>;
<span class="pl-k">import</span> <span class="pl-smi">exp</span> <span class="pl-k">from</span> <span class="pl-s"><span
                class="pl-pds">'</span>circleplus<span class="pl-pds">'</span></span>;
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-en">exp</span>(<span class="pl-smi">math</span>.<span
                class="pl-smi">e</span>));</pre>
    </div>
    <p>上面代码中的<code>import exp</code>表示，将<code>circleplus</code>模块的默认方法加载为<code>exp</code>方法。</p>
    <h2><a href="#跨模块常量"  class="anchor" id="user-content-跨模块常量">

    </a>跨模块常量
    </h2>
    <p>本书介绍<code>const</code>命令的时候说过，<code>const</code>声明的常量只在当前代码块有效。如果想设置跨模块的常量（即跨多个文件），或者说一个值要被多个模块共享，可以采用下面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> constants.js 模块</span>
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-c1">A</span> <span
                class="pl-k">=</span> <span class="pl-c1">1</span>;
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-c1">B</span> <span
                class="pl-k">=</span> <span class="pl-c1">3</span>;
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-c1">C</span> <span
                class="pl-k">=</span> <span class="pl-c1">4</span>;

<span class="pl-c"><span class="pl-c">//</span> test1.js 模块</span>
<span class="pl-k">import</span> <span class="pl-c1">*</span> <span class="pl-k">as</span> <span class="pl-smi">constants</span> <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>./constants<span
                class="pl-pds">'</span></span>;
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">constants</span>.<span
                class="pl-c1">A</span>); <span class="pl-c"><span class="pl-c">//</span> 1</span>
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">constants</span>.<span
                class="pl-c1">B</span>); <span class="pl-c"><span class="pl-c">//</span> 3</span>

<span class="pl-c"><span class="pl-c">//</span> test2.js 模块</span>
<span class="pl-k">import</span> {<span class="pl-smi">A</span>, <span class="pl-smi">B</span>} <span
                class="pl-k">from</span> <span class="pl-s"><span class="pl-pds">'</span>./constants<span
                class="pl-pds">'</span></span>;
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">A</span>); <span
                class="pl-c"><span class="pl-c">//</span> 1</span>
<span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">B</span>); <span
                class="pl-c"><span class="pl-c">//</span> 3</span></pre>
    </div>
    <p>如果要使用的常量非常多，可以建一个专门的<code>constants</code>目录，将各种常量写在不同的文件里面，保存在该目录下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> constants/db.js</span>
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-c1">db</span> <span
                class="pl-k">=</span> {
  url<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>http://my.couchdbserver.local:5984<span
                class="pl-pds">'</span></span>,
  admin_username<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>admin<span
                class="pl-pds">'</span></span>,
  admin_password<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>admin password<span
                class="pl-pds">'</span></span>
};

<span class="pl-c"><span class="pl-c">//</span> constants/user.js</span>
<span class="pl-k">export</span> <span class="pl-k">const</span> <span class="pl-c1">users</span> <span
                class="pl-k">=</span> [<span class="pl-s"><span class="pl-pds">'</span>root<span class="pl-pds">'</span></span>, <span
                class="pl-s"><span class="pl-pds">'</span>admin<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>staff<span class="pl-pds">'</span></span>, <span class="pl-s"><span
                class="pl-pds">'</span>ceo<span class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>chief<span
                class="pl-pds">'</span></span>, <span class="pl-s"><span class="pl-pds">'</span>moderator<span
                class="pl-pds">'</span></span>];</pre>
    </div>
    <p>然后，将这些文件输出的常量，合并在<code>index.js</code>里面。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span
            class="pl-c">//</span> constants/index.js</span>
<span class="pl-k">export</span> {<span class="pl-smi">db</span>} <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./db<span class="pl-pds">'</span></span>;
<span class="pl-k">export</span> {<span class="pl-smi">users</span>} <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./users<span class="pl-pds">'</span></span>;</pre>
    </div>
    <p>使用的时候，直接加载<code>index.js</code>就可以了。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> script.js</span>
<span class="pl-k">import</span> {<span class="pl-smi">db</span>, <span class="pl-smi">users</span>} <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./index<span class="pl-pds">'</span></span>;</pre>
    </div>
    <h2><a href="#import"  class="anchor" id="user-content-import">

    </a>import()
    </h2>
    <h3><a href="#简介"  class="anchor" id="user-content-简介">

    </a>简介
    </h3>
    <p>前面介绍过，<code>import</code>命令会被 JavaScript 引擎静态分析，先于模块内的其他模块执行（叫做”连接“更合适）。所以，下面的代码会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c"><span class="pl-c">//</span> 报错</span>
<span class="pl-k">if</span> (x <span class="pl-k">===</span> <span class="pl-c1">2</span>) {
  <span class="pl-k">import</span> <span class="pl-smi">MyModual</span> <span class="pl-k">from</span> <span
                class="pl-s"><span class="pl-pds">'</span>./myModual<span class="pl-pds">'</span></span>;
}</pre>
    </div>
    <p>上面代码中，引擎处理<code>import</code>语句是在编译时，这时不会去分析或执行<code>if</code>语句，所以<code>import</code>语句放在<code>if</code>代码块之中毫无意义，因此会报句法错误，而不是执行时错误。也就是说，<code>import</code>和<code>export</code>命令只能在模块的顶层，不能在代码块之中（比如，在<code>if</code>代码块之中，或在函数之中）。
    </p>
    <p>这样的设计，固然有利于编译器提高效率，但也导致无法在运行时加载模块。在语法上，条件加载就不可能实现。如果<code>import</code>命令要取代 Node 的<code>require</code>方法，这就形成了一个障碍。因为<code>require</code>是运行时加载模块，<code>import</code>命令无法取代<code>require</code>的动态加载功能。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">path</span> <span class="pl-k">=</span> <span class="pl-s"><span
            class="pl-pds">'</span>./<span class="pl-pds">'</span></span> <span class="pl-k">+</span> fileName;
<span class="pl-k">const</span> <span class="pl-c1">myModual</span> <span class="pl-k">=</span> <span class="pl-c1">require</span>(path);</pre>
    </div>
    <p>上面的语句就是动态加载，<code>require</code>到底加载哪一个模块，只有运行时才知道。<code>import</code>语句做不到这一点。</p>
    <p>因此，有一个<a href="https://github.com/tc39/proposal-dynamic-import">提案</a>，建议引入<code>import()</code>函数，完成动态加载。</p>
    <div class="highlight highlight-source-js">
        <pre><span class="pl-k">import</span>(specifier)</pre>
    </div>
    <p>
        上面代码中，<code>import</code>函数的参数<code>specifier</code>，指定所要加载的模块的位置。<code>import</code>命令能够接受什么参数，<code>import()</code>函数就能接受什么参数，两者区别主要是后者为动态加载。
    </p>
    <p><code>import()</code>返回一个 Promise 对象。下面是一个例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span
            class="pl-c1">main</span> <span class="pl-k">=</span> <span class="pl-c1">document</span>.<span
            class="pl-c1">querySelector</span>(<span class="pl-s"><span class="pl-pds">'</span>main<span class="pl-pds">'</span></span>);

<span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">`</span>./section-modules/<span
                class="pl-s1"><span class="pl-pse">${</span>someVariable<span class="pl-pse">}</span></span>.js<span
                class="pl-pds">`</span></span>)
  .<span class="pl-en">then</span>(<span class="pl-smi">module</span> <span class="pl-k">=&gt;</span> {
    <span class="pl-c1">module</span>.<span class="pl-en">loadPageInto</span>(main);
  })
  .<span class="pl-en">catch</span>(<span class="pl-smi">err</span> <span class="pl-k">=&gt;</span> {
    <span class="pl-smi">main</span>.<span class="pl-smi">textContent</span> <span class="pl-k">=</span> <span
                class="pl-smi">err</span>.<span class="pl-smi">message</span>;
  });</pre>
    </div>
    <p><code>import()</code>函数可以用在任何地方，不仅仅是模块，非模块的脚本也可以使用。它是运行时执行，也就是说，什么时候运行到这一句，也会加载指定的模块。另外，<code>import()</code>函数与所加载的模块没有静态连接关系，这点也是与<code>import</code>语句不相同。
    </p>
    <p><code>import()</code>类似于 Node 的<code>require</code>方法，区别主要是前者是异步加载，后者是同步加载。</p>
    <h3><a href="#适用场合"  class="anchor" id="user-content-适用场合">

    </a>适用场合
    </h3>
    <p>下面是<code>import()</code>的一些适用场合。</p>
    <p>（1）按需加载。</p>
    <p><code>import()</code>可以在需要的时候，再加载某个模块。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">button</span>.<span class="pl-c1">addEventListener</span>(<span
            class="pl-s"><span class="pl-pds">'</span>click<span class="pl-pds">'</span></span>, <span class="pl-smi">event</span> <span
            class="pl-k">=&gt;</span> {
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./dialogBox.js<span
                class="pl-pds">'</span></span>)
  .<span class="pl-en">then</span>(<span class="pl-smi">dialogBox</span> <span class="pl-k">=&gt;</span> {
    <span class="pl-smi">dialogBox</span>.<span class="pl-c1">open</span>();
  })
  .<span class="pl-en">catch</span>(<span class="pl-smi">error</span> <span class="pl-k">=&gt;</span> {
    <span class="pl-c"><span class="pl-c">/*</span> Error handling <span class="pl-c">*/</span></span>
  })
});</pre>
    </div>
    <p>上面代码中，<code>import()</code>方法放在<code>click</code>事件的监听函数之中，只有用户点击了按钮，才会加载这个模块。</p>
    <p>（2）条件加载</p>
    <p><code>import()</code>可以放在<code>if</code>代码块，根据不同的情况，加载不同的模块。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">if</span> (condition) {
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>moduleA<span class="pl-pds">'</span></span>).<span
                class="pl-en">then</span>(<span class="pl-k">...</span>);
} <span class="pl-k">else</span> {
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>moduleB<span class="pl-pds">'</span></span>).<span
                class="pl-en">then</span>(<span class="pl-k">...</span>);
}</pre>
    </div>
    <p>上面代码中，如果满足条件，就加载模块 A，否则加载模块 B。</p>
    <p>（3）动态的模块路径</p>
    <p><code>import()</code>允许模块路径动态生成。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span>(<span class="pl-en">f</span>())
.<span class="pl-en">then</span>(<span class="pl-k">...</span>);</pre>
    </div>
    <p>上面代码中，根据函数<code>f</code>的返回结果，加载不同的模块。</p>
    <h3><a href="#注意点"  class="anchor" id="user-content-注意点">

    </a>注意点
    </h3>
    <p><code>import()</code>加载模块成功以后，这个模块会作为一个对象，当作<code>then</code>方法的参数。因此，可以使用对象解构赋值的语法，获取输出接口。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span>(<span class="pl-s"><span
            class="pl-pds">'</span>./myModule.js<span class="pl-pds">'</span></span>)
.<span class="pl-en">then</span>(({export1, export2}) <span class="pl-k">=&gt;</span> {
  <span class="pl-c"><span class="pl-c">//</span> ...·</span>
});</pre>
    </div>
    <p>上面代码中，<code>export1</code>和<code>export2</code>都是<code>myModule.js</code>的输出接口，可以解构获得。</p>
    <p>如果模块有<code>default</code>输出接口，可以用参数直接获得。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span>(<span class="pl-s"><span
            class="pl-pds">'</span>./myModule.js<span class="pl-pds">'</span></span>)
.<span class="pl-en">then</span>(<span class="pl-smi">myModule</span> <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-smi">myModule</span>.<span
                class="pl-smi">default</span>);
});</pre>
    </div>
    <p>上面的代码也可以使用具名输入的形式。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">import</span>(<span class="pl-s"><span
            class="pl-pds">'</span>./myModule.js<span class="pl-pds">'</span></span>)
.<span class="pl-en">then</span>(({default<span class="pl-k">:</span> theDefault}) <span class="pl-k">=&gt;</span> {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(theDefault);
});</pre>
    </div>
    <p>如果想同时加载多个模块，可以采用下面的写法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module1.js<span
                class="pl-pds">'</span></span>),
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module2.js<span
                class="pl-pds">'</span></span>),
  <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module3.js<span
                class="pl-pds">'</span></span>),
])
.<span class="pl-en">then</span>(([<span class="pl-smi">module1</span>, <span class="pl-smi">module2</span>, <span
                class="pl-smi">module3</span>]) <span class="pl-k">=&gt;</span> {
   ···
});</pre>
    </div>
    <p><code>import()</code>也可以用在 async 函数之中。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">async</span> <span
            class="pl-k">function</span> <span class="pl-en">main</span>() {
  <span class="pl-k">const</span> <span class="pl-c1">myModule</span> <span class="pl-k">=</span> <span class="pl-k">await</span> <span
                class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./myModule.js<span
                class="pl-pds">'</span></span>);
  <span class="pl-k">const</span> {<span class="pl-c1">export1</span>, <span class="pl-c1">export2</span>} <span
                class="pl-k">=</span> <span class="pl-k">await</span> <span class="pl-k">import</span>(<span
                class="pl-s"><span class="pl-pds">'</span>./myModule.js<span class="pl-pds">'</span></span>);
  <span class="pl-k">const</span> [<span class="pl-c1">module1</span>, <span class="pl-c1">module2</span>, <span
                class="pl-c1">module3</span>] <span class="pl-k">=</span>
    <span class="pl-k">await</span> <span class="pl-c1">Promise</span>.<span class="pl-c1">all</span>([
      <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module1.js<span class="pl-pds">'</span></span>),
      <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module2.js<span class="pl-pds">'</span></span>),
      <span class="pl-k">import</span>(<span class="pl-s"><span class="pl-pds">'</span>./module3.js<span class="pl-pds">'</span></span>),
    ]);
}
<span class="pl-en">main</span>();</pre>
    </div>
</div>
</body>
</html>
